package database

import (
	"database/sql"
	"strings"
)

type Transaction struct {
	tx *sql.Tx
	IsPanic bool
}

// commit transaction
func (this *Transaction) Commit() error {
	return this.tx.Commit()
}

// rollback transaction
func (this *Transaction) Rollback() error {
	return this.tx.Rollback()
}

// query rows with sql and params
func (this *Transaction) Query(sqlStr string, args... interface {}) ([]map[string]string, error) {
	// query rows
	rows, e := this.tx.Query(sqlStr, args...)
	if e != nil {
		return nil, panicError(this.IsPanic, sqlStr, e)
	}
	// close row data resource
	defer rows.Close()
	// get columns
	rs, e2 := rowMapper(rows)
	return rs, panicError(this.IsPanic, sqlStr, e2)
}

// query one row
func (this *Transaction) One(sqlStr string, args... interface {}) (map[string]string, error) {
	// if no limit keyword in sqlStr, add it
	if !strings.Contains(sqlStr, "LIMIT 1") {
		sqlStr = sqlStr + " LIMIT 1"
	}
	// query and return first one
	// do not panic here, if set panic, it will throw panic when Query method
	rs, e := this.Query(sqlStr, args...)
	if e != nil {
		return nil, e
	}
	if len(rs) < 1 {
		return nil, nil
	}
	return rs[0], nil
}

// exec sqlStr and return insertId(insert) or affectRows(update,delete)
func (this *Transaction) Exec(sqlStr string, args... interface {}) (int64, error) {
	rs , e := this.tx.Exec(sqlStr, args...)
	if e != nil {
		return -1, panicError(this.IsPanic, sqlStr, e)
	}
	var i int64
	i, e = resultParser(rs)
	return i, panicError(this.IsPanic, sqlStr, e)
}

// get new transaction statement from prepared statement
// support db's cached stmt struct, and make it work in transaction as a new statement with old setting
// this new transaction stmt won't be cached
func (this *Transaction) Stmt(stmt *Statement) *Statement {
	newStmt := this.tx.Stmt(stmt.sqlStmt)
	s := &Statement{}
	s.QueryString = stmt.QueryString
	s.sqlStmt = newStmt
	s.isPanic = stmt.isPanic
	return s
}

// get transaction statement by preparing sqlStr
// this statement won't be cached
func (this *Transaction) Prepare(sqlStr string) (*Statement, error) {
	s := &Statement{}
	s.QueryString = sqlStr
	var e error
	s.sqlStmt, e = this.tx.Prepare(sqlStr)
	if e != nil {
		return nil, panicError(this.IsPanic, sqlStr, e)
	}
	s.isPanic = this.IsPanic
	return s, nil
}

// get raw *sql.Tx struct
func (this *Transaction) SqlTx() *sql.Tx {
	return this.tx
}


